/* FragRag (C) Copyright 1990-2008 Bill Buckels */

/* previously revised Oct 14, 1991 */
/* last revised May 2008 */

/* converts from an IBM Bsaved PUT to an apple image fragment */
/* extension is RAG */

/* also outputs a run length encoded version of the RAG */
/* extension is RAX */


/* also outputs a text version of the RAG and RAX */
/* suitable for embedding in Aztec C programs */
/* extension is RAT */


/* this is an extended version of FragRag which
   creates runlength encoded files in addition to the
   standard output of raw files created by the
   previous version.

   The size improvement varies and can be minimal and
   may not be worth the effort of decoding these,

   This version has other additional features that
   are not present in the orginal, but is a
   programmer's utility so not written in a
   bulletproof manner.

    */

/* The FRAGGLE.EXE utility will produce PUT format
   images from CGA 4-Color BSaved Files which are
   freely available on the IBM-PC. Clipshop can be
   used to create 4 Color BSaved Files from the
   Windows Clipboard and other sources as can several
   old utilites. A version of FRAGGLE is included with
   this program.

   To learn more about all this visit my wikipedia
   page at

http://en.wikipedia.org/wiki/BSAVE_%28graphics_image_format%29

   Use FRAGGLE.EXE to create the PUT image fragments
   that you will be using in your Apple II programs.

   Download Clipshop at

   www.clipshop.ca

   */

#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <dos.h>
#include <bios.h>
#include <malloc.h>
#include <string.h>
#include <graph.h>

#define bitsperbyte 8

/* default palette is orange, blue */
/* allow base palette to be changed to green, violet from the
   command line */
/* allow palette change for specific rasters using mapping file
   of the same base name as the image fragment */
unsigned char paltype = 0x80;
unsigned char palettemap[192];

/* map the 4 color CGA to the Apple palette
   color 0 - black
   color 1 - cyan to blue
   color 2 - magenta to orange
   color 3 - white

   allow an optional color reversal of blue and orange
*/

unsigned char blue = 1, orange = 2;

int reverse = 0;
int clip = 0;
int batch = 0;

unsigned char encbuf[8194];

/* pcx encoder helper */
/* the writer for the encline function */
int encput(unsigned char byt,unsigned char cnt, FILE *pcxfile)
{
          if(cnt){
            if((cnt==1)&& (0xc0 != (0xc0 &byt))){
                if(EOF == fputc((int)byt,pcxfile))
                return(0);
                return(1);
            }
            else{
                if(EOF==fputc((int)0xc0|cnt,pcxfile))
                return(0);
                if(EOF==fputc((int)byt,pcxfile))
                return(0);
                return(2);
            }
        }
        return(0);

}

/* encode a line in pcxformat encoding */
/* encodes a raw line and writes it out to disk */
int encline(unsigned char *inbuff,int inlen, FILE *pcxfile)
{
    unsigned char this,last;
    int srcindex,i;
    int total;
    unsigned char runcount;
    total=0;
    last = *(inbuff); runcount=1;

for(srcindex=1;srcindex!=inlen;srcindex++){
    this= *(++inbuff);
    if(this==last){
        runcount++;
        if(runcount==63){
            if(!(i=encput(last,runcount,pcxfile)))
            return(0);
            total+=i;
            runcount=0;
        }
    }
    else{
        if(runcount){
            if(!(i=encput(last,runcount,pcxfile)))
            return(0);
            total+=i;
        }
        last=this;
        runcount=1;
      }
   }

   if(runcount){
    if(!(i=encput(last,runcount,pcxfile)))
    return(0);
    return(total+i);
    }
    return (total);

}

unsigned int tempwidth, tempheight;

int fragin(char *fragment)
{

    FILE *fp;
    int fh;
    unsigned char far *buffer;
    unsigned char far *crt= (char *)0xb8000000l;
    unsigned int width, height;
    unsigned int buffersize,headerlength=7;
    unsigned char floodcolor, ch;

    fp=fopen(fragment,"rb");
    if (NULL == fp)return -1;
    buffersize=filelength(fileno(fp))-headerlength;
    fclose(fp);

    buffer=_fmalloc(buffersize);
    if (NULL == buffer)return -2;

    fh = open(fragment,O_RDONLY|O_BINARY);
    if (fh == -1) {
	   _ffree(buffer);
      return -3;
	}
    read(fh,buffer,7);
    if (buffer[0] != 0xfd) {
		close(fh);
		 _ffree(buffer);
		return -4;
	}
    read(fh,buffer,buffersize);
    close(fh);

   width=(buffer[0]|buffer[1]<<8) ;

   tempwidth=width/2;

   if((width%bitsperbyte)!=0)
                width=(width/bitsperbyte)+1;
   else width=(width/bitsperbyte) ;

   height=(buffer[2]|buffer[3]<<8);
   tempheight = height;

   if (clip == 1) {
   		floodcolor=buffer[4];
   		memset(crt,floodcolor,0x4000);
   }

    _putimage(0,0,buffer,_GPSET);

   if (reverse)
   	 for (height = 0; height < 0x4000; height++)
   	   crt[height] ^= 0xff;

    _ffree(buffer);

    if (batch == 0) {
		/* a little menu here to reverse back again etc.
		   add commands as required */
		while (batch == 0) {
		     ch = toupper(getch());
		     switch(ch) {
				 case 0: getch(); batch = 1; break;/* clear arrow keys */
				 case 'R':
				  for (height = 0; height < 0x4000; height++)
   	   				crt[height] ^= 0xff;
   	   				break;
   	   			 case 27:
   	   	            return -5;
   	   			 default:
   	   			    batch = 1;
   	   			    break;

			 }
		}

	}
    return 0;

}


/* return the color value of the pixel at x, y */
unsigned char getpixel(int x, int y)
{
    union REGS inregs, outregs;

    inregs.h.ah = '\x0d';
    inregs.x.cx = x;
    inregs.x.dx = y;

    int86(0x10, &inregs,&outregs);
    return outregs.h.al;
}



int OrangeBlackWhite(char *buf0, char *name, char *name2, char *name3, char *mapname)
{

	FILE *fp, *fp2, *fp3, *fmap;

	int pcxctr = 0;
	char bytebuf[41];
	unsigned char inbuf[282],outbuf[282];
	unsigned char bit[7];
	unsigned char bytewidth,rasters;
	int pixels;
	int topx=0,botx=0,topy=0,boty=0;
	int x,xx,xxx,y,bytes;
	char ch;
	int value, height,idx;
	unsigned int xsize;

	if(tempwidth%14 !=0)
	  botx  = topx+((tempwidth/14)*14)+14; /* multiple of 14 */
	else
	  botx  = topx+tempwidth;

	boty = topy + tempheight;

	pixels    = (botx-topx)+1;
	rasters   = tempheight;
	bytewidth = (unsigned char)(pixels/7);

	fp = fopen(name,"wb");
	if (NULL == fp)return -1;
	fp2 = fopen(name2,"wb");
	if (NULL == fp2) {
	   fclose(fp);
	   return -1;
	}


   fp3 = fopen(name3,"w");


   if (NULL != fp3) {
	    /* the c array contains width in bytes, height in rasters
	    followed by data */
	    fprintf(fp3,"/* %s.rat Embedded Graphics Image Created by\n"
	                "   FragRag(C) Copyright Bill Buckels 1990-2008 */\n\n",buf0);

	    fprintf(fp3,"/* suitable for embedding in Aztec C Apple II Programs */\n");
	    fprintf(fp3,"/* width in bytes, height in rasters, data... */\n\n");
   }

   /* create a header for the RAG and RAX files */
   /* meaning is raster width in bytes x number of rasters */

     fputc(bytewidth,fp);
     fputc(rasters,fp);
     fputc(bytewidth,fp2);
     fputc(rasters,fp2);

   /*
   use an external mapping file to allow for override
   of orange or green palette for selected rasters
   otherwise the same base palette will be used
   for the entire fragment

   each line in the mapping file should either be
   y=,v=
   or
   y=,v=,h=

   this mapping will override any base palette
	*/
   for (y=0; y < 192; y++)
        palettemap[y] = paltype; /* set default */


   fmap=fopen(mapname,"r");

   /* the following mapping does not check for overlaps
      FIFO logic is used. The next mapping will overwrite
      the previous mapping if any overlaps. */

   if (NULL != fmap) {
     while (NULL != fgets(inbuf, 128, fmap)){
	     y = -1;
	     value = paltype;
	     height = 1; /* height if omitted assumed 1 row */
	     for (idx = 0; inbuf[idx] != 0;idx++) {
		    switch(toupper(inbuf[idx]))
		    {
				case 'H': if (inbuf[idx+1] != '=')break;
				          height= atoi(&inbuf[idx+2]);
				          /* heights can be higher
				             than screen width
				             but never lower than 1 */
				          if (height < 1)height = 0;
				          if (height > 191)height = 191;
				          break;

				          /* if y is out of range no remap */
				case 'Y': if (inbuf[idx+1] != '=')break;
				          y= atoi(&inbuf[idx+2]);
				          if (y < 0 || y > 191)y = -1;
				          break;

				case 'V': if (inbuf[idx+1] != '=')break;
				          ch = toupper(inbuf[idx+2]);
				          if (ch == 'O') {
							  value = 0x80; break;
						  }
						  if (ch == 'G') {
							  value = 0; break;
						  }
						  /* only zero will map to green */
				          value= atoi(&inbuf[idx+2]);
				          if (value != 0)value = 0x80;
				          break;
			}

		 }
		 if (y > -1 && y < 192 && height > 0) {
			  height = height + y;
		      for (x = y; x < height; x++) {
			      if (x > 191)break;
			      palettemap[y] = (unsigned char)value;
			  }

		 }
      }
      fclose(fmap);

   }


   for(y=topy;y<boty;y++)
   {

     /* start by assuming everything is black*/
     for(x=0;x<282;x++)outbuf[x]=0;
     for(x=0;x<41;x++)bytebuf[x]=0;

     /* next accumulate the white values */

     for(x=0;x<pixels;x++)
     {
       inbuf[x]=getpixel(x+topx,y);
       if(inbuf[x] == 3)outbuf[x]  = 1;
       }

      /* next check for secondary color */
      inbuf[280]=inbuf[279];
      xx=1;
      xxx=2;
      for(x=0;x<pixels;x++)
      {
        /* if we have blue write it out */
        /* on the ibm, paired blue pixels will result in
           a blue pixel on the apple. the same logic holds
           true for orange. results will vary of course. */

        if(inbuf[x]  == blue && inbuf[xx]  == blue)
           {
           if(x%2==0)
               {
               outbuf[x]  = 1;
               outbuf[xx] = 0;
               }
           else
           {
              outbuf[xx]  =  1;
              outbuf[xxx] =  0;
           }
           }

          /* and if we have orange write it out */
        if(inbuf[x]  == orange && inbuf[xx]  == orange )
           {
           if(x%2==0)
               {
               outbuf[x]  = 0;
               outbuf[xx] = 1;
               }
           else
           {
              outbuf[xx]  =  0;
              outbuf[xxx] =  1;
           }
           }
           xx++;
           xxx++;
           }

         /*  now pack the raster and write out the file */

          paltype = palettemap[y];
          x = 0;
          for(bytes=0;bytes<bytewidth;bytes++)
          {
          for(xx=0;xx<7;xx++)
          {
            bit[xx]=outbuf[x];
            x++;
            }
          bytebuf[bytes]=(paltype     |bit[6]<<6|bit[5]<<5|bit[4]<<4|
                          bit[3]<<3|bit[2]<<2|bit[1]<<1|bit[0]);
          fputc(bytebuf[bytes],fp);
          encbuf[pcxctr] = bytebuf[bytes];
          pcxctr++;
          }

       /* make another pass and loop until finished */
      }
      fclose(fp);



     if(NULL != fp3) {
       fprintf(fp3,"#define RAW 1\n\n#ifdef RAW\n");
       fprintf(fp3,"/* raw array of Apple II RAG image */\n");
       fprintf(fp3,"unsigned char %s[%d] = {\n%3d,%3d,",buf0,pcxctr+2,bytewidth,rasters);
       fflush(fp3);
       y=0;
       idx = 2;
       for (y = 0; y < pcxctr; y++) {
		  x=encbuf[y];
	      fprintf(fp3,"%3d",x);
	      if(y == (pcxctr-1))fprintf(fp3,"};");
	      else fputc(',',fp3);
          idx++;
	      if (idx==11) {
			  fputc('\n',fp3);
			  fflush(fp3);
			  idx=0;
		  }
	   }

	 }


     if (NULL != fp2){
		/* write the encoded file */
	  	encline((unsigned char *)&encbuf[0],pcxctr,fp2);
	  	fclose(fp2);
	 }

     /* write the encoded C array */
	 if (NULL != fp3){
       	fp2 = fopen(name2,"rb");
       	if (NULL != fp2) {
			xsize =filelength(fileno(fp2));
			rewind(fp2);
			fprintf(fp3,"\n#else\n");
			fprintf(fp3,"/* encoded array of Apple II RAG image */\n");
	        fprintf(fp3,"unsigned char %s[%u] = {\n",buf0,xsize);
	        idx = 0;
       	    x= fgetc(fp2);
       		fprintf(fp3,"%3d",x);
        	do {
		   		x = fgetc(fp2);
		   		if (x == EOF) {
			   		fprintf(fp3,"};\n");
			   		break;
		   		}
		   		fputc(',',fp3);
		   		idx++;
		   		if (idx == 11) {
		      		fputc('\n',fp3);
		      		fflush(fp3);
		      		idx = 0;
		   		}
		   		fprintf(fp3,"%3d",x);

	   	    }while (x!=EOF);

       		fclose(fp2);
		}
		fprintf(fp3,"#endif\n");
       	fclose(fp3);

	 }
     return 0;

}

main(int argc,char *argv[])
{
   int idx;
   char buf0[66],buf1[66],buf2[66],buf3[66],buf4[66];
   char *wordptr;
   char c;

   paltype = 0x80;
   clip = 0;

   if (argc < 2) {
      puts("FragRag (C) Copyright Bill Buckels 1990-2008");
      puts("All Rights Reserved.");
      puts("Usage is \"FRAGRAG MY.PUT\"");
      puts("Option   \"FRAG64X MY.PUT G\" - Violet Green Palette");
      puts("Option   \"FRAG64X MY.PUT O\" - Blue Orange Palette (default)");
      puts("Option   \"FRAG64X MY.PUT F\" - Flip Blue to Orange, Orange to Blue");
      puts("Option   \"FRAG64X MY.PUT B\" - Batch Mode (do not pause for keypress)");
      puts("Option   \"FRAG64X MY.PUT R\" - Reverse Image");
      puts("Option   \"FRAG64X MY.PUT OFBR\" - Options can be combined.");
      puts("Outputs  \"MY.RAG MY.RAX MY.RAT\" Raw, Encoded and Embedded Apple II Graphic");
      puts("Read the source code for additional options (mapping file etc.).");
   }
   else {
	   strcpy(buf0,argv[1]);
	   strcpy(buf1,buf0);
	   if (argc > 2) {
          strcpy(buf2,argv[2]);
          for (idx = 0; buf2[idx] != 0; idx++) {
		     c = toupper(buf2[idx]);
		     switch(c) {
			   case 'G': paltype = 0; break;
			   case 'O': paltype = 0x80; break;
			   case 'C': clip = 1; break;
			   case 'F': blue = 2;
			             orange = 1;
			             break;
			   case 'B': batch = 1; break;
			   case 'R': reverse = 1; break;
			 }

		  }
		}
		_setvideomode(5);
		if(fragin(buf1) == 0) {
			wordptr=strtok(buf0,".");
			sprintf(buf1,"%s.RAG",buf0);
			sprintf(buf2,"%s.RAX",buf0);
			sprintf(buf3,"%s.RAT",buf0);
			sprintf(buf4,"%s.MAP",buf0);
			OrangeBlackWhite(buf0,buf1,buf2,buf3,buf4);
		}
		_setvideomode(3);
	}
    exit(0);
}
